feat(config): 配置项与内容统一分离——支持从内容仓库覆盖配置 - #551
Merged
Dawn6666666 merged 5 commits intoAug 11, 2026
Merged
Conversation
配置项此前必须直接改代码仓库的 src/config/*.ts,fork 上游的用户每次合并 上游更新都要在配置文件上解冲突。现在把配置也纳入内容分离机制: - sync-content 新增 overrides -> src/config/overrides 映射; - src/config/index.ts 改为合并适配器,导出前把同名覆盖深合并进默认配置, 各配置文件保持上游原版; - 合并语义为对象深合并、数组整体替换,覆盖缺失时行为与现状完全一致; - 覆盖文件用 satisfies DeepPartial<XxxConfig> 约束,字段拼错或类型不符在 构建期报错。 同时修正三处会绕过合并入口的读取点:SITE_LANG 改为从合并后的 siteConfig 派生,image-utils 改走 @/config 入口,update-anime/bangumi/bilibili 通过 新增的 read-site-config 助手优先读取覆盖值。
- 新增 pnpm export-config:以 git 上的上游版本为基准,把 src/config/ 里
改过的字段导出成最小化的 overrides/*.ts。导出前自检
deepMerge(上游默认, 覆盖) 能否还原当前配置,对不上会指出是哪个配置;
navBarConfig 的 LinkPreset 按枚举名序列化,深合并表达不了的删除键单独报出。
- read-site-config 改为花括号配平的块内取值。此前的惰性正则在部分覆盖文件上
会串块:overrides 里写 anime:{} 且后面有 font:{mode:"system"} 时,番剧模式
会被读成 "system" 而跳过数据更新。同时 coverMirror / useWebp 也收敛到
bilibili 块内,不再全局匹配。
- 覆盖块里没写的字段继续回退默认值,保证「只覆盖 vmid、coverMirror 取默认」
这类部分覆盖成立;番剧模式仍由 update-anime 路由 + 子脚本自检双重保证互斥。
- 补充 tests/site-config-reader.test.ts 覆盖上述回退矩阵。
- 文档补上完整迁移步骤(导出 → 放进内容仓库 → 还原 src/config → 同步校验 →
触发部署 → 回滚)。
覆盖文件是带相对导入的 TS 模块,sync-content 建立的 junction 会被 Vite 解析到内容仓库的真实路径,../../types/config 在真实路径下不存在,astro build 直接失败(CI 克隆内容仓库到 ./content 时同样会命中)。改为复制同步:源目录缺失时清理旧副本,避免失效配置残留;文档同步更新。
Dawn6666666
force-pushed
the
feat/issue-549-config-overrides
branch
from
August 11, 2026 02:09
0d276b1 to
3d9270a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
Closes #549
Mizuki 已经有了很完善的内容分离(
ENABLE_CONTENT_SYNC+sync-content),文章、页面数据、图片都可以放进独立仓库。但src/config/下的配置项仍然必须直接改代码仓库的源文件。对于 fork 上游、定期跟进版本更新的用户,配置文件是升级冲突的重灾区:上游每次调整配置结构,都会和本地的个人值冲突,需要逐个人工解决。实测参照:一个真实的个人站点相对上游改动了 9 个配置文件、约 630 行,每次合并上游都要在这些文件上处理冲突。
本 PR 把配置也纳入同一套分离机制:
src/config/*.ts保持上游原版,个人值放进内容仓库的overrides/,构建时深合并。方案
overrides/siteConfig.ts覆盖siteConfig),共 18 个合法名src/config/overrides/已加入.gitignore,不进入代码仓库提交历史overrides/时,import.meta.glob返回空对象,所有配置等同于上游默认值,行为与现状逐字节一致合并语义
nullundefined的键举例:默认
banner.carousel是{ enable: true, interval: 3, switchable: true },覆盖里只写{ interval: 8 },结果是{ enable: true, interval: 8, switchable: true }。类型安全
覆盖文件用
satisfies DeepPartial<XxxConfig>约束。这里必须是DeepPartial而不是 issue 里最初设想的Partial——Partial<T>只让顶层键可选,而SiteConfig的嵌套块字段大多必填(themeColor的hue/fixed、banner.carousel的enable/interval/switchable),写carousel: { interval: 8 }会直接编译报错,恰恰是部分覆盖最常见的写法。tsconfig.json的include: src/**/*已经覆盖同步目标目录,所以pnpm type-check会检查覆盖文件。三类错误都能在构建期拦住:一键导出:
pnpm export-config已经改了一堆配置的用户不用手抄。该命令以 git 上的上游版本为基准(自动依次尝试
upstream/master→upstream/main→origin/master→origin/main,可用--ref=指定,--out=改输出目录),把当前src/config/与那一版逐字段比对,只导出改过的字段到overrides-export/(已 gitignore):三个要点:
deepMerge(上游默认, 覆盖) === 当前配置,对不上就报出是哪个配置、差在哪,不会产出「看着像但装上不一样」的覆盖navBarConfig.links里的LinkPreset按枚举名序列化(LinkPreset.Home),不是裸数字改动清单
新增
src/config/deepMerge.tsnode --experimental-strip-types单测src/config/overrideLoader.tsimport.meta.glob加载 + 18 名白名单校验scripts/export-config.mjsscripts/read-site-config.mjstests/config-overrides.test.tstests/site-config-reader.test.ts修改
src/config/index.tswidgetConfigs聚合合并后的对象src/types/config.tsDeepPartial<T>(数组分支短路,避免string[]退化成(string | undefined)[])scripts/sync-content.jscontentMappings增加一条overrides → src/config/overrides(复制同步;符号链接会让 Vite 把相对导入解析到内容仓库真实路径导致构建失败,未采用).gitignore/src/config/overrides/与/overrides-export/package.jsonexport-config脚本docs/CONTENT_SEPARATION.mddocs/CONTENT_REPOSITORY.mdoverrides/顺带修正的一致性问题
实现过程中发现 4 处会让覆盖静默失效或读到错值的地方,都在本 PR 内修掉:
1.
src/utils/image-utils.ts绕过合并入口原来直接
import { siteConfig } from "../config/siteConfig",导致imageOptimization的formats/quality/noReferrerDomains覆盖完全不生效。改为走../config。改完之后全仓库已无绕过合并入口的直接 import。2.
SITE_LANG不跟随合并结果siteConfig.ts里const SITE_LANG是独立常量,index.ts原样 re-export。覆盖siteConfig.lang后SITE_LANG仍是上游默认值。改为从合并后的siteConfig.lang派生。3. 番剧数据脚本读不到覆盖
update-anime/update-bangumi/update-bilibili用正则扒siteConfig.ts源码取anime.mode、bangumi.userId、bilibili.vmid。这些恰恰是纯个人值,放进 overrides 后不生效,而update-anime.mjs就在build脚本链的第一环。统一改走read-site-config.mjs,按「覆盖 → 默认」顺序取值。4. 惰性正则在部分覆盖文件上会串块(这条是真实 bug)
原来的
/anime:\s*\{[\s\S]*?mode:\s*["']([^"']+)["']/在完整的默认配置上没问题,但覆盖文件是部分配置且键序任意。实测复现:番剧模式被读成
"system",update-anime.mjs直接跳过数据更新。改成花括号配平的块内取值,同时把coverMirror/useWebp也从全局匹配收敛进bilibili块内。番剧模式的互斥性不受影响:
update-anime.mjs路由 + 子脚本各自自检,两边读同一个值,实测覆盖成bilibili后update-bangumi.mjs输出Detected current anime mode is "bilibili", skipping。验证
基础门禁
pnpm type-check→ 0 error(与本 PR 之前完全一致)pnpm astro check→ 322 files,0 errors / 0 warnings / 0 hintspnpm build→ 通过biome ci干净机制验证
overrides/时<title>为上游默认值,与现状一致banner.carousel.interval,enable/switchable保留默认banner.src.desktop覆盖为 1 张,结果就是 1 张;兄弟键mobile的 4 张不受影响export defaultsync-content,overrides 复制进代码仓库,合并结果正确bilibili.vmid时,coverMirror/useWebp/bangumi.userId全部正确回退默认真实规模端到端验收
拿一个真实个人站点的配置(9 个分歧文件)走完整流程:
pnpm export-config→ 自动生成 9 个最小化覆盖文件,自检通过git checkout <上游ref> -- src/config/→ 配置还原成上游原版src/config/overrides/pnpm type-check→ 0 errorpnpm build→ 站点标题、profile 链接、导航链接(与 profile 是不同的 B 站号,证明各配置独立合并)全部正确,默认 banner 残留 0 处深合并能 100% 表达该站点的个人配置,没有出现「上游有、个人版删掉」这类无法表达的键。
一个能说明机制价值的观察:把基线从旧版本更新到最新上游后,
siteConfig需要覆盖的顶层键从 18 个降到 16 个——上游已经合入的字段自动从覆盖里消失。跟上游跟得越紧,覆盖越小。迁移步骤
文档里写了完整流程,简述:
内容仓库的
trigger-build.yml需要把overrides/**加进paths,否则只改配置不会触发重新构建。回滚:覆盖机制是纯叠加的,删掉
overrides/里的文件重新同步即可回到上游默认值;想完全退回旧方式,把个人配置写回src/config/*.ts,代码不需要任何改动。已知限制
已写进文档:
pnpm export-config遇到会明确报出来siteConfig.lang。commentConfig.ts在模块顶层引用语言常量填充 Twikoo / Giscus 的lang,覆盖siteConfig.lang时需要同时提供overrides/commentConfig.ts@/config入口,直接 import 某个配置文件会绕过合并src/config/overrides/在每次 dev/build 前从内容仓库复制而来;dev 运行中修改覆盖文件需要重启pnpm dev才会重新同步scripts/compress-fonts/暂不读取覆盖值,该目录是独立的手动工具,不在pnpm build流程内兼容性
ENABLE_CONTENT_SYNCoverrides/目录时行为与现状完全一致,可按需渐进采用,逐个配置迁移package.json的test脚本,跟随现有约定(tests/下多数文件也不在默认脚本里)更新记录(2026-08-11)
overrides/同步由符号链接改为复制:覆盖文件是带相对导入的 TS 模块,junction 会被 Vite 解析到内容仓库真实路径,../../types/config找不到导致astro build失败(CI 克隆内容仓库到./content时同样复现),原描述「Vite glob 能穿透」已修正overrides/后,同步会清理代码仓库里的旧副本,避免失效配置残留